﻿using DotNetty.Transport.Channels;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace DotNetty.Wraper
{
    abstract class BaseTcpSocketServer<TSocketServer, TConnection, TData> : IBaseTcpSocketServer<TConnection>, IChannelEvent
        where TConnection : class, IBaseSocketConnection
        where TSocketServer : class, IBaseTcpSocketServer<TConnection>
    {
        public BaseTcpSocketServer(int port, TcpSocketServerEvent<TSocketServer, TConnection, TData> eventHandle)
        {
            Port = port;
            _eventHandle = eventHandle;
        }

        #region 私有成员

        ConcurrentDictionary<string, TConnection> _idMapConnections { get; }
            = new ConcurrentDictionary<string, TConnection>();
        ConcurrentDictionary<string, string> _nameMapId { get; }
            = new ConcurrentDictionary<string, string>();
        protected IChannel _serverChannel { get; set; }
        protected void AddConnection(TConnection theConnection)
        {
            _idMapConnections.TryAdd(theConnection.ConnectionId, theConnection);
            _nameMapId.TryAdd(theConnection.ConnectionName, theConnection.ConnectionId);
        }
        protected TConnection GetConnection(IChannel clientChannel)
        {
            return GetConnectionById(clientChannel.Id.AsShortText());
        }
        protected void PackException(Action action)
        {
            try
            {
                action?.Invoke();
            }
            catch (Exception ex)
            {
                _eventHandle.OnException?.Invoke(ex);
            }
        }
        protected TcpSocketServerEvent<TSocketServer, TConnection, TData> _eventHandle { get; }
        protected abstract TConnection BuildConnection(IChannel clientChannel);

        #endregion

        #region 外部接口
        public void OnChannelRegistered(IChannel channel)
        {
            _eventHandle.OnChannelRegistered?.Invoke(this as TSocketServer, channel);
        }
        public void OnChannelUnregistered(IChannel channel)
        {
            PackException(() =>
            {
                _eventHandle.OnChannelUnregistered?.Invoke(this as TSocketServer, null, channel);
            });
           
        }
        public void OnChannelActive(IChannelHandlerContext ctx)
        {
            var theConnection = BuildConnection(ctx.Channel);
            AddConnection(theConnection);
            _eventHandle.OnNewConnection?.Invoke(this as TSocketServer, theConnection);
        }

        public void OnException(IChannel clientChannel, Exception ex)
        {
            PackException(() =>
            {
                var theConnection = GetConnection(clientChannel);
                if (theConnection != null)
                {
                    CloseConnection(theConnection);
                }
                _eventHandle.OnException?.Invoke(ex);
            });
        }

        public void OnChannelInactive(IChannel clientChannel)
        {
            PackException(() =>
            {
                var theConnection = GetConnection(clientChannel);
                if (theConnection != null)
                {
                    RemoveConnection(theConnection);
                    _eventHandle.OnConnectionClose?.Invoke(this as TSocketServer, theConnection);
                }
            });
        }

        public void SetChannel(IChannel channel)
        {
            _serverChannel = channel;
        }

        public int Port { get; }

        public List<TConnection> GetAllConnections()
        {
            return _idMapConnections.Values.Select(x => x as TConnection).ToList();
        }

        public TConnection GetConnectionById(string connectionId)
        {
            if (_idMapConnections.TryGetValue(connectionId, out var connection))
                return connection;
            else return default(TConnection);
            //return _idMapConnections[connectionId];
        }

        public TConnection GetConnectionByName(string connectionName)
        {
            return _idMapConnections[_nameMapId[connectionName]];
        }

        public List<string> GetAllConnectionNames()
        {
            return _nameMapId.Keys.ToList();
        }

        public int GetConnectionCount()
        {
            return _idMapConnections.Count;
        }

        public void RemoveConnection(TConnection theConnection)
        {
            _idMapConnections.TryRemove(theConnection.ConnectionId, out _);
            _nameMapId.TryRemove(theConnection.ConnectionName, out _);
        }

        public void SetConnectionName(TConnection theConnection, string oldConnectionName, string newConnectionName)
        {
            _nameMapId.TryRemove(oldConnectionName, out _);
            _nameMapId[newConnectionName] = theConnection.ConnectionId;
        }

        public void CloseConnection(TConnection theConnection)
        {
            theConnection?.Close();
        }

        public System.Action BeforeClose { get; set; }
        public void Close()
        {
            BeforeClose?.Invoke();
            _serverChannel?.CloseAsync();
            AfterClose?.Invoke();
        }
        public System.Action AfterClose { get; set; }

        public abstract void OnChannelReceive(IChannelHandlerContext ctx, object msg);

        #endregion

        #region Send
        /// <summary>
        /// 发送字节
        /// </summary>
        /// <param name="bytes">字节数组</param>
        /// <param name="clientid">目标地址</param>
        /// <returns></returns>
        public async Task Send(byte[] bytes, string clientid)
        {
            if (string.IsNullOrWhiteSpace(clientid))
            {
                GetAllConnections()?.ForEach(async f => await f.Send(bytes));
            }
            else
            {
                var theConnection = GetConnectionById(clientid);
                if (theConnection != null)
                {
                    await theConnection.Send(bytes);
                }
            }
        }

        /// <summary>
        /// 发送字符串,UTF-8编码
        /// </summary>
        /// <param name="msgStr">字符串</param>
        /// <param name="clientid">目标地址</param>
        /// <returns></returns>
        public async Task Send(string msgStr, string clientid)
        {
            await Send(msgStr.GetBytes(), clientid);
        }
        #endregion

    }
}